
// Size Wrapper
//  Size(width, height)
//  Size([width, height])
var Size = function(width, height) {
  var size = [0, 0];
  var functions = Size.functions;
  for (f in functions) {
    if (functions.hasOwnProperty(f)) {
      size[f] = functions[f];
    }
  }

  if (width instanceof Array) {
    size[0] = width[0];
    size[1] = width[1];
  } else if (typeof(width) === "number" && typeof(height) === "number") {
    size[0] = width;
    size[1] = height;
  }
  
  return size;
};

Size.functions = {
  setWidth : function (width) {
    this[0] = width;
    return this;
  },
  setHeight : function (height) {
    this[1] = height;
    return this;
  },
  getWidth : function () {
    return this[0];
  },
  getHeight : function () {
    return this[1];
  },
  getArea : function () {
    return this[0] * this[1];
  },
  widerThan : function (size) {
    return this[0] > size[0];
  },
  tallerThan : function (size) {
    return this[1] > size[1];
  },
  largerAreaThan : function (size) {
    return this.getArea() > size.getArea();
  }
};


// Point Wrapper
//  Point(x, y)
//  Point([x, y])
var Point = function(x, y) {
  var point = [0, 0];
  
  var functions = Point.functions;
  for (f in functions) {
    if (functions.hasOwnProperty(f)) {
      point[f] = functions[f];
    }
  }

  if (x instanceof Array) {
    [point[0], point[1]] = x;
  } else if (typeof(x) === "number" && typeof(y) === "number") {
    point[0] = x;
    point[1] = y;
  }
  
  return point;
};

Point.functions = {
  setX : function (x) {
    this[0] = x;
    return this;
  },
  setY : function (y) {
    this[1] = y;
    return this;
  },
  getX : function () {
    return this[0];
  },
  getY : function () {
    return this[1];
  }
};


// Rectangle Wrapper
//  Rectangle(x, y, width, height)
//  Rectangle(point, size)
//  Rectangle([x, y], [width, height])
//  Rectangle([x, y, width, height])
function Rectangle(x, y, width, height) {
  var rect = [0, 0, 0, 0];

  var functions = Rectangle.functions;
  for (f in functions) {
    if (functions.hasOwnProperty(f)) {
      rect[f] = functions[f];
    }
  }

  if (typeof(x.setX) === "function" && typeof(y.setWidth) === "function") {
    rect[0] = x[0];
    rect[1] = x[1];
    rect[2] = y[0];
    rect[3] = y[1];
  } else if (x instanceof Array && y instanceof Array) {
    rect[0] = x[0];
    rect[1] = x[1];
    rect[2] = y[0];
    rect[3] = y[1];
  } else if (typeof(x) === "number" && typeof(y) === "number" && typeof(width) === "number" && typeof(height) === "number")  {
    rect[0] = x;
    rect[1] = y;
    rect[2] = width;
    rect[3] = height;
  } else if (x instanceof Array && x.length >= 4) {
    rect[0] = x[0];
    rect[1] = x[1];
    rect[2] = x[2];
    rect[3] = x[3];
  }
  
  return rect;
};
Rectangle.functions = {
  setX : function (x) {
    this[0] = x;
    return this;
  },
  setY : function (y) {
    this[1] = y;
    return this;
  },
  getX : function () {
    return this[0];
  },
  getY : function () {
    return this[1];
  },
  setWidth : function (width) {
    this[2] = width;
    return this;
  },
  setHeight : function (height) {
    this[3] = height;
    return this;
  },
  getWidth : function () {
    return this[2];
  },
  getHeight : function () {
    return this[3];
  },
  setPoint : function (point) {
    this[0] = point[0];
    this[1] = point[1];
    return this;
  },
  setSize : function (size) {
    this[2] = size[0];
    this[3] = size[1];
    return this;
  },
  getPoint : function () {
    return Point(this[0], this[1]);
  },
  getSize : function () {
    return Size(this[2], this[3]);
  },
  containsPoint : function (point) {
    return (point[0] >= this[0] &&
      point[1] >= this[1] &&
      point[0] <= this[0] + this[2] &&
      point[1] <= this[1] + this[3]);
  },
  containsRect : function (rect) {
    var rX1 = rect[0], rX2 = rX1 + rect[2], rY1 = rect[1], rY2 = rY1 + rect[3];
    var tX1 = this[0], tX2 = tX1 + this[2], tY1 = this[1], tY2 = tY1 + this[3];
    
    return (rX1 >= tX1 &&
      rY1 >= tY1 &&
      rX2 <= tX2 &&
      rY2 <= tY2);
  },
  insideRect : function (rect) {
    var rX1 = rect[0], rX2 = rX1 + rect[2], rY1 = rect[1], rY2 = rY1 + rect[3];
    var tX1 = this[0], tX2 = tX1 + this[2], tY1 = this[1], tY2 = tY1 + this[3];
    
    return (rX1 <= tX1 &&
      rY1 <= tY1 &&
      rX2 >= tX2 &&
      rY2 >= tY2);
  },
  overlapsRect : function (rect) {
    var rX1 = rect[0], rX2 = rX1 + rect[2], rY1 = rect[1], rY2 = rY1 + rect[3];
    var tX1 = this[0], tX2 = tX1 + this[2], tY1 = this[1], tY2 = tY1 + this[3];
    
    return (tX1 <= rX2 &&
      tY1 <= rY2 &&
      tX2 >= rX1 &&
      tY2 >= rY1);
  },
  centerHorizontallyIn : function (rect) {
    this[0] = Math.floor((rect[2] - this[2]) / 2);
    
    return this;
  },
  centerVerticallyIn : function (rect) {
    this[1] = Math.floor((rect[3] - this[3]) / 2);
    
    return this;
  },
  centerIn : function (rect) {
    this.centerHorizontallyIn(rect);
    this.centerVerticallyIn(rect);
    
    return this;
  }
};

// Color Wrapper
// Parameters:
//  Color(red, green, blue, alpha = 1)
//  Color([red, green, blue], alpha = 1)
//  Color([red, green, blue, alpha])
var Color = function(red, green, blue, alpha) {
  var color = [1, 1, 1, 1];
  
  var functions = Color.functions;
  for (f in functions) {
    if (functions.hasOwnProperty(f)) {
      color[f] = functions[f];
    }
  }

  if (red instanceof Array && typeof(green) === "number") {
    [color[0], color[1], color[2], color[3]] = [red[0], red[1], red[2], alpha];
  } else if (red instanceof Array && red.length === 3) {
    [color[0], color[1], color[2], color[3]] = [red[0], red[1], red[2], 1];
  } else if (red instanceof Array && red.length >= 4) {
    [color[0], color[1], color[2], color[3]] = red;
  } else if (typeof(red) === "number" && typeof(green) === "number" && typeof(blue) === "number" && typeof(alpha) === "undefined") {
    [color[0], color[1], color[2], color[3]]  = [red, green, blue, 1];
  } else if (typeof(red) === "number" && typeof(green) === "number" && typeof(blue) === "number" && typeof(alpha) === "number") {
    [color[0], color[1], color[2], color[3]] = [red, green, blue, alpha];
  }
  
  return color;
};

Color.functions = {
  setRed : function (red) {
    this[0] = red;
    return this;
  },
  setGreen : function (green) {
    this[1] = green;
    return this;
  },
  setBlue : function (blue) {
    this[2] = blue;
    return this;
  },
  setAlpha : function (alpha) {
    this[3] = alpha;
    return this;
  },
  setRGB : function (rgb) {
    [this[0], this[1], this[2]] = rgb;
    return this;
  },
  getRed : function () {
    return this[0];
  },
  getGreen : function () {
    return this[1];
  },
  getBlue : function () {
    return this[2];
  },
  getAlpha : function () {
    return this[3];
  },
  getRGB : function () {
    return [this[0], this[1], this[2]];
  }
};
Color.red = Color(1, 0, 0);
Color.blue = Color(0, 0, 1);
Color.green = Color(0, 1, 0);
Color.black = Color(0, 0, 0);
Color.white = Color(1, 1, 1);
Color.gray = Color(0.5, 0.5, 0.5);
Color.transparent = Color(0, 0, 0, 0);